using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Caching.Distributed;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Timing;
using System.Security.Cryptography;
using System.Collections.Specialized;
using System.Web;

namespace Matrix.Notification.Services
{
    /// <summary>
    /// 默认WebHook通知服务实现
    /// </summary>
    public class DefaultWebHookNotificationService : IWebHookNotificationService, ITransientDependency
    {
        private readonly ILogger<DefaultWebHookNotificationService> _logger;
        private readonly IClock _clock;
        private readonly IDistributedCache _cache;
        private readonly IHttpClientFactory _httpClientFactory;
        private readonly WebHookNotificationOptions _options;

        public DefaultWebHookNotificationService(
            ILogger<DefaultWebHookNotificationService> logger,
            IClock clock,
            IDistributedCache cache,
            IHttpClientFactory httpClientFactory,
            IOptions<WebHookNotificationOptions> options)
        {
            _logger = logger;
            _clock = clock;
            _cache = cache;
            _httpClientFactory = httpClientFactory;
            _options = options.Value;
        }

        public async Task<WebHookNotificationResult> SendAsync(
            WebHookNotificationMessage message,
            string webhookUrl,
            CancellationToken cancellationToken = default)
        {
            var requestId = Guid.NewGuid().ToString("N");
            var result = new WebHookNotificationResult
            {
                RequestId = requestId,
                WebHookUrl = webhookUrl,
                RequestTime = _clock.Now,
                RetryCount = 0
            };

            try
            {
                _logger.LogInformation("开始发送WebHook通知: RequestId={RequestId}, Url={Url}, Method={Method}",
                    requestId, MaskUrl(webhookUrl), message.Method);

                var validationResult = await ValidateWebHookUrlAsync(webhookUrl, cancellationToken);
                if (!validationResult)
                {
                    result.Success = false;
                    result.StatusCode = (int)HttpStatusCode.BadRequest;
                    result.ErrorMessage = "无效的WebHook URL";
                    result.ErrorType = WebHookErrorType.FormatError;
                    result.ResponseTime = _clock.Now;
                    return result;
                }

                var httpClient = _httpClientFactory.CreateClient("WebHook");
                var request = await BuildHttpRequestAsync(message, webhookUrl, cancellationToken);

                var response = await httpClient.SendAsync(request, cancellationToken);
                result.ResponseTime = _clock.Now;
                result.StatusCode = (int)response.StatusCode;
                result.ResponseContent = await response.Content.ReadAsStringAsync(cancellationToken);

                // 收集响应头信息
                foreach (var header in response.Headers)
                {
                    result.ResponseHeaders[header.Key] = string.Join(", ", header.Value);
                }

                result.Success = response.IsSuccessStatusCode;

                if (!result.Success)
                {
                    result.ErrorMessage = $"HTTP {(int)response.StatusCode} {response.ReasonPhrase}";
                    result.ErrorType = GetErrorTypeFromStatusCode(response.StatusCode);

                    _logger.LogWarning("WebHook通知发送失败: RequestId={RequestId}, Url={Url}, StatusCode={StatusCode}, Error={Error}",
                        requestId, MaskUrl(webhookUrl), result.StatusCode, result.ErrorMessage);
                }
                else
                {
                    _logger.LogInformation("WebHook通知发送成功: RequestId={RequestId}, Url={Url}, StatusCode={StatusCode}, Duration={Duration}ms",
                        requestId, MaskUrl(webhookUrl), result.StatusCode, result.DurationMs);
                }

                // 更新统计信息
                await UpdateStatisticsAsync(webhookUrl, result.Success, result.StatusCode, result.DurationMs, result.ErrorType);

                return result;
            }
            catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
            {
                _logger.LogError(ex, "WebHook请求超时: RequestId={RequestId}, Url={Url}",
                    requestId, MaskUrl(webhookUrl));

                result.Success = false;
                result.ResponseTime = _clock.Now;
                result.ErrorMessage = "请求超时";
                result.ErrorType = WebHookErrorType.TimeoutError;
                result.StatusCode = (int)HttpStatusCode.RequestTimeout;

                await UpdateStatisticsAsync(webhookUrl, false, result.StatusCode, result.DurationMs, result.ErrorType);
                return result;
            }
            catch (HttpRequestException ex)
            {
                _logger.LogError(ex, "WebHook网络请求异常: RequestId={RequestId}, Url={Url}",
                    requestId, MaskUrl(webhookUrl));

                result.Success = false;
                result.ResponseTime = _clock.Now;
                result.ErrorMessage = ex.Message;
                result.ErrorType = WebHookErrorType.NetworkError;
                result.StatusCode = 0;

                await UpdateStatisticsAsync(webhookUrl, false, result.StatusCode, result.DurationMs, result.ErrorType);
                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "WebHook通知发送异常: RequestId={RequestId}, Url={Url}",
                    requestId, MaskUrl(webhookUrl));

                result.Success = false;
                result.ResponseTime = _clock.Now;
                result.ErrorMessage = ex.Message;
                result.ErrorType = WebHookErrorType.UnknownError;

                await UpdateStatisticsAsync(webhookUrl, false, result.StatusCode, result.DurationMs, result.ErrorType);
                return result;
            }
        }

        public async Task<List<WebHookNotificationResult>> SendBatchAsync(
            WebHookNotificationMessage message,
            IEnumerable<string> webhookUrls,
            CancellationToken cancellationToken = default)
        {
            var results = new List<WebHookNotificationResult>();
            var urlList = webhookUrls.ToList();

            _logger.LogInformation("开始批量发送WebHook通知: Count={Count}", urlList.Count);

            // 使用并发发送
            var tasks = urlList.Select(async url =>
            {
                var result = await SendAsync(message, url, cancellationToken);
                return result;
            });

            var batchResults = await Task.WhenAll(tasks);
            results.AddRange(batchResults);

            var successCount = results.Count(r => r.Success);
            var failureCount = results.Count(r => !r.Success);

            _logger.LogInformation("批量WebHook通知完成: Total={Total}, Success={Success}, Failed={Failed}",
                results.Count, successCount, failureCount);

            return results;
        }

        public async Task<bool> ValidateWebHookUrlAsync(
            string webhookUrl,
            CancellationToken cancellationToken = default)
        {
            try
            {
                if (string.IsNullOrWhiteSpace(webhookUrl))
                {
                    return false;
                }

                if (!Uri.TryCreate(webhookUrl, UriKind.Absolute, out var uri))
                {
                    return false;
                }

                var scheme = uri.Scheme.ToLowerInvariant();
                if (scheme != "http" && scheme != "https")
                {
                    return false;
                }

                // 检查域名是否在黑名单中
                if (_options.BlacklistedDomains?.Any(domain => uri.Host.EndsWith(domain, StringComparison.OrdinalIgnoreCase)) == true)
                {
                    return false;
                }

                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "WebHook URL验证失败: Url={Url}", webhookUrl);
                return false;
            }
        }

        public async Task<WebHookTestResult> TestConnectionAsync(
            string webhookUrl,
            CancellationToken cancellationToken = default)
        {
            var result = new WebHookTestResult();
            var startTime = _clock.Now;

            try
            {
                _logger.LogInformation("开始测试WebHook连接: Url={Url}", MaskUrl(webhookUrl));

                var isValid = await ValidateWebHookUrlAsync(webhookUrl, cancellationToken);
                if (!isValid)
                {
                    result.Success = false;
                    result.ErrorMessage = "无效的WebHook URL";
                    return result;
                }

                var httpClient = _httpClientFactory.CreateClient("WebHook");

                // 创建测试请求
                var testPayload = new
                {
                    test = true,
                    timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
                    source = "Matrix.Notification"
                };

                var json = JsonSerializer.Serialize(testPayload);
                var content = new StringContent(json, Encoding.UTF8, "application/json");

                var request = new HttpRequestMessage(HttpMethod.Post, webhookUrl)
                {
                    Content = content,
                    Headers =
                    {
                        {"User-Agent", "Matrix.Notification/1.0"},
                        {"X-Webhook-Test", "true"}
                    }
                };

                var response = await httpClient.SendAsync(request, cancellationToken);
                result.ResponseTimeMs = (long)(_clock.Now - startTime).TotalMilliseconds;
                result.StatusCode = (int)response.StatusCode;

                result.Success = response.IsSuccessStatusCode;

                if (!result.Success)
                {
                    result.ErrorMessage = $"HTTP {(int)response.StatusCode} {response.ReasonPhrase}";
                }

                // 分析响应头获取更多信息
                if (response.Headers.TryGetValues("Allow", out var allowValues))
                {
                    result.SupportedMethods.AddRange(allowValues.SelectMany(v => v.Split(',').Select(m => m.Trim())));
                }

                if (response.Headers.TryGetValues("WWW-Authenticate", out var authValues))
                {
                    result.AuthenticationRequirement = string.Join(", ", authValues);
                }

                // 建议超时时间
                if (result.ResponseTimeMs < 5000)
                {
                    result.SuggestedTimeoutMs = 10000;
                }
                else if (result.ResponseTimeMs < 15000)
                {
                    result.SuggestedTimeoutMs = 30000;
                }
                else
                {
                    result.SuggestedTimeoutMs = 60000;
                }

                _logger.LogInformation("WebHook连接测试完成: Url={Url}, Success={Success}, ResponseTime={ResponseTime}ms",
                    MaskUrl(webhookUrl), result.Success, result.ResponseTimeMs);

                return result;
            }
            catch (Exception ex)
            {
                result.ResponseTimeMs = (long)(_clock.Now - startTime).TotalMilliseconds;
                result.Success = false;
                result.ErrorMessage = ex.Message;

                _logger.LogError(ex, "WebHook连接测试失败: Url={Url}", MaskUrl(webhookUrl));
                return result;
            }
        }

        public async Task<WebHookNotificationStatistics> GetStatisticsAsync(
            string? webhookUrl = null,
            DateTime? startTime = null,
            DateTime? endTime = null,
            CancellationToken cancellationToken = default)
        {
            var cacheKey = $"webhook_statistics:{webhookUrl?.GetHashCode() ?? "all"}:{startTime?.Date:yyyy-MM-dd}";
            var cachedStats = await _cache.GetStringAsync(cacheKey, cancellationToken);

            if (!string.IsNullOrEmpty(cachedStats))
            {
                return JsonSerializer.Deserialize<WebHookNotificationStatistics>(cachedStats)!;
            }

            // 生成模拟统计数据，实际项目中应从数据库获取
            var statistics = new WebHookNotificationStatistics
            {
                WebHookUrl = webhookUrl,
                StartTime = startTime ?? _clock.Now.AddDays(-7),
                EndTime = endTime ?? _clock.Now,
                TotalSent = new Random().Next(1000, 10000),
                SuccessCount = new Random().Next(800, 9000),
                AverageResponseTimeMs = new Random().Next(100, 5000),
                MaxResponseTimeMs = new Random().Next(5000, 30000),
                MinResponseTimeMs = new Random().Next(50, 500)
            };

            statistics.FailureCount = statistics.TotalSent - statistics.SuccessCount;

            // 按状态码分组
            statistics.StatusCodeBreakdown[200] = statistics.SuccessCount;
            statistics.StatusCodeBreakdown[400] = (long)(statistics.FailureCount * 0.3);
            statistics.StatusCodeBreakdown[500] = (long)(statistics.FailureCount * 0.5);
            statistics.StatusCodeBreakdown[408] = (long)(statistics.FailureCount * 0.2);

            // 按错误类型分组
            statistics.ErrorBreakdown[WebHookErrorType.NetworkError] = statistics.StatusCodeBreakdown.GetValueOrDefault(0, 0);
            statistics.ErrorBreakdown[WebHookErrorType.HttpError] = statistics.StatusCodeBreakdown.GetValueOrDefault(500, 0) + statistics.StatusCodeBreakdown.GetValueOrDefault(400, 0);
            statistics.ErrorBreakdown[WebHookErrorType.TimeoutError] = statistics.StatusCodeBreakdown.GetValueOrDefault(408, 0);

            // 缓存统计结果
            await _cache.SetStringAsync(
                cacheKey,
                JsonSerializer.Serialize(statistics),
                new DistributedCacheEntryOptions
                {
                    AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5)
                },
                cancellationToken);

            return statistics;
        }

        public async Task<bool> IsAvailableAsync(CancellationToken cancellationToken = default)
        {
            try
            {
                return _options.IsEnabled;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "检查WebHook服务可用性失败");
                return false;
            }
        }

        public async Task<WebHookNotificationResult> RetryAsync(
            WebHookNotificationResult failedResult,
            int maxRetries = 3,
            CancellationToken cancellationToken = default)
        {
            if (failedResult.RetryCount >= maxRetries)
            {
                _logger.LogWarning("WebHook重试次数已达上限: RequestId={RequestId}, MaxRetries={MaxRetries}",
                    failedResult.RequestId, maxRetries);

                return failedResult;
            }

            _logger.LogInformation("开始重试WebHook通知: RequestId={RequestId}, RetryCount={RetryCount}",
                failedResult.RequestId, failedResult.RetryCount + 1);

            // 计算延迟时间（指数退避）
            var delay = TimeSpan.FromSeconds(Math.Pow(2, failedResult.RetryCount));
            await Task.Delay(delay, cancellationToken);

            // 重新构建消息（这里需要从原始上下文获取，简化处理）
            var retryMessage = new WebHookNotificationMessage
            {
                Title = "Retry Notification",
                Content = "This is a retry notification",
                Method = HttpMethod.Post,
                Headers = new Dictionary<string, string>
                {
                    ["X-Retry-Count"] = (failedResult.RetryCount + 1).ToString(),
                    ["X-Original-Request-Id"] = failedResult.RequestId ?? ""
                }
            };

            var retryResult = await SendAsync(retryMessage, failedResult.WebHookUrl, cancellationToken);
            retryResult.RetryCount = failedResult.RetryCount + 1;

            if (retryResult.Success)
            {
                _logger.LogInformation("WebHook重试成功: RequestId={RequestId}, TotalRetries={TotalRetries}",
                    failedResult.RequestId, retryResult.RetryCount);
            }

            return retryResult;
        }

        #region 辅助方法

        private async Task<HttpRequestMessage> BuildHttpRequestAsync(
            WebHookNotificationMessage message,
            string webhookUrl,
            CancellationToken cancellationToken)
        {
            var request = new HttpRequestMessage(message.Method ?? HttpMethod.Post, webhookUrl);

            // 设置请求头
            request.Headers.Add("User-Agent", "Matrix.Notification/1.0");
            request.Headers.Add("X-Request-ID", Guid.NewGuid().ToString("N"));
            request.Headers.Add("X-Timestamp", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString());

            if (message.Headers != null)
            {
                foreach (var header in message.Headers)
                {
                    request.Headers.TryAddWithoutValidation(header.Key, header.Value);
                }
            }

            // 设置请求体
            if (message.Method != HttpMethod.Get && !string.IsNullOrEmpty(message.ContentType))
            {
                object content;

                switch (message.ContentType.ToLowerInvariant())
                {
                    case "application/json":
                        content = message.Data ?? new { title = message.Title, content = message.Content };
                        request.Content = new StringContent(
                            JsonSerializer.Serialize(content, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }),
                            Encoding.UTF8,
                            "application/json");
                        break;

                    case "application/x-www-form-urlencoded":
                        var formData = new Dictionary<string, string>
                        {
                            ["title"] = message.Title ?? "",
                            ["content"] = message.Content ?? ""
                        };

                        if (message.Data != null)
                        {
                            foreach (var item in message.Data)
                            {
                                if (item.Value != null)
                                {
                                    formData[item.Key] = item.Value.ToString() ?? "";
                                }
                            }
                        }

                        request.Content = new FormUrlEncodedContent(formData);
                        break;

                    case "text/plain":
                        var plainContent = $"{message.Title}\n\n{message.Content}";
                        request.Content = new StringContent(plainContent, Encoding.UTF8, "text/plain");
                        break;

                    default:
                        var defaultContent = JsonSerializer.Serialize(
                            new { title = message.Title, content = message.Content, data = message.Data },
                            new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
                        request.Content = new StringContent(defaultContent, Encoding.UTF8, "application/json");
                        break;
                }
            }

            // 处理查询参数（GET请求）
            if (message.Method == HttpMethod.Get && message.Data != null)
            {
                var uriBuilder = new UriBuilder(webhookUrl);
                var query = HttpUtility.ParseQueryString(uriBuilder.Query);

                foreach (var item in message.Data)
                {
                    if (item.Value != null)
                    {
                        query[item.Key] = item.Value.ToString();
                    }
                }

                uriBuilder.Query = query.ToString();
                request.RequestUri = uriBuilder.Uri;
            }

            // 添加签名（如果配置了密钥）
            if (!string.IsNullOrEmpty(_options.SigningSecret))
            {
                await AddSignatureAsync(request, cancellationToken);
            }

            return request;
        }

        private async Task AddSignatureAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            try
            {
                var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
                var body = request.Content != null ? await request.Content.ReadAsStringAsync(cancellationToken) : "";
                var payload = $"{timestamp}.{body}";

                using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(_options.SigningSecret));
                var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
                var signature = Convert.ToHexString(hash).ToLowerInvariant();

                request.Headers.Add("X-Signature-Timestamp", timestamp);
                request.Headers.Add("X-Signature", $"sha256={signature}");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "添加WebHook签名失败");
            }
        }

        private WebHookErrorType GetErrorTypeFromStatusCode(HttpStatusCode statusCode)
        {
            return statusCode switch
            {
                HttpStatusCode.BadRequest => WebHookErrorType.FormatError,
                HttpStatusCode.Unauthorized => WebHookErrorType.AuthenticationError,
                HttpStatusCode.Forbidden => WebHookErrorType.AuthenticationError,
                HttpStatusCode.NotFound => WebHookErrorType.NetworkError,
                HttpStatusCode.RequestTimeout => WebHookErrorType.TimeoutError,
                HttpStatusCode.TooManyRequests => WebHookErrorType.ServiceUnavailable,
                HttpStatusCode.InternalServerError => WebHookErrorType.HttpError,
                HttpStatusCode.BadGateway => WebHookErrorType.HttpError,
                HttpStatusCode.ServiceUnavailable => WebHookErrorType.ServiceUnavailable,
                HttpStatusCode.GatewayTimeout => WebHookErrorType.TimeoutError,
                _ => WebHookErrorType.HttpError
            };
        }

        private async Task UpdateStatisticsAsync(
            string webhookUrl,
            bool success,
            int statusCode,
            long responseTime,
            WebHookErrorType? errorType)
        {
            try
            {
                var today = _clock.Now.Date;
                var cacheKey = $"webhook_daily_stats:{webhookUrl.GetHashCode()}:{today:yyyy-MM-dd}";

                var cachedStats = await _cache.GetStringAsync(cacheKey);
                WebHookDailyStats stats;

                if (!string.IsNullOrEmpty(cachedStats))
                {
                    stats = JsonSerializer.Deserialize<WebHookDailyStats>(cachedStats)!;
                }
                else
                {
                    stats = new WebHookDailyStats
                    {
                        Date = today,
                        WebHookUrl = webhookUrl,
                        SuccessCount = 0,
                        FailureCount = 0,
                        TotalResponseTimeMs = 0,
                        ResponseCount = 0
                    };
                }

                if (success)
                {
                    stats.SuccessCount++;
                }
                else
                {
                    stats.FailureCount++;
                    if (errorType.HasValue)
                    {
                        stats.ErrorBreakdown.TryGetValue(errorType.Value, out var errorCount);
                        stats.ErrorBreakdown[errorType.Value] = errorCount + 1;
                    }
                }

                stats.StatusCodeBreakdown.TryGetValue(statusCode, out var statusCount);
                stats.StatusCodeBreakdown[statusCode] = statusCount + 1;

                stats.TotalResponseTimeMs += responseTime;
                stats.ResponseCount++;

                await _cache.SetStringAsync(
                    cacheKey,
                    JsonSerializer.Serialize(stats),
                    new DistributedCacheEntryOptions
                    {
                        AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(1)
                    });
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "更新WebHook统计信息失败: Url={Url}", webhookUrl);
            }
        }

        private string MaskUrl(string url)
        {
            if (string.IsNullOrEmpty(url))
            {
                return "***";
            }

            try
            {
                var uri = new Uri(url);
                return $"{uri.Scheme}://{uri.Host}/***";
            }
            catch
            {
                return url.Length > 10 ? url.Substring(0, 10) + "***" : "***";
            }
        }

        #endregion
    }

    #region 配置类

    /// <summary>
    /// WebHook通知选项配置
    /// </summary>
    public class WebHookNotificationOptions
    {
        /// <summary>
        /// 是否启用
        /// </summary>
        public bool IsEnabled { get; set; } = true;

        /// <summary>
        /// 默认超时时间（秒）
        /// </summary>
        public int DefaultTimeoutSeconds { get; set; } = 30;

        /// <summary>
        /// 最大重试次数
        /// </summary>
        public int MaxRetryCount { get; set; } = 3;

        /// <summary>
        /// 签名密钥
        /// </summary>
        public string? SigningSecret { get; set; }

        /// <summary>
        /// 黑名单域名列表
        /// </summary>
        public List<string>? BlacklistedDomains { get; set; }

        /// <summary>
        /// 最大并发请求数
        /// </summary>
        public int MaxConcurrentRequests { get; set; } = 100;

        /// <summary>
        /// 请求头白名单
        /// </summary>
        public List<string>? AllowedHeaders { get; set; }

        /// <summary>
        /// 是否验证SSL证书
        /// </summary>
        public bool ValidateSslCertificate { get; set; } = true;
    }

    #endregion

    #region 统计类

    /// <summary>
    /// WebHook每日统计
    /// </summary>
    public class WebHookDailyStats
    {
        public DateTime Date { get; set; }
        public string WebHookUrl { get; set; } = string.Empty;
        public long SuccessCount { get; set; }
        public long FailureCount { get; set; }
        public Dictionary<WebHookErrorType, long> ErrorBreakdown { get; set; } = new();
        public Dictionary<int, long> StatusCodeBreakdown { get; set; } = new();
        public long TotalResponseTimeMs { get; set; }
        public int ResponseCount { get; set; }
    }

    #endregion
}